/* Copyright  2009
   @Author
   Richard Changde Yin            e-mail yinchangde@hotmail.com

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
/**
 * @filename  sql_connect.cc
 */


/*
  Functions to authenticate and handle requests for a connection
*/
#include <server_includes.h>

#include <error.h>
#include <sql_parse.h>
#include <scheduling.h>
#include <session.h>
#include <connect.h>


#include <databaseManager.h>
#include <databaseManagerImpl.h>
#include <error.h>

using namespace BackEnd;
/*
  Thread handler for a connection

  SYNOPSIS
    handle_one_connection()
    arg      Connection object (Session)

  IMPLEMENTATION
    This function (normally) does the following:
    - Initialize thread
    - Initialize Session to be used with this thread
    - Authenticate user
    - Execute all queries sent on the connection
    - Take connection down
    - End thread  / Handle next connection using thread from thread cache
*/
pthread_handler_t handle_one_connection(void *arg)
{
  Session *session= static_cast<Session*>(arg);

  Scheduler &thread_scheduler= get_thread_scheduler();
  if (thread_scheduler.init_new_connection_thread())
  {
    session->disconnect(ER_OUT_OF_RESOURCES, true);
    statistic_increment(aborted_connects,&LOCK_status);
    thread_scheduler.end_thread(session,0);
    return 0;
  }

  /*
    handle_one_connection() is normally the only way a thread would
    start and would always be on the very high end of the stack ,
    therefore, the thread stack always starts at the address of the
    first local variable of handle_one_connection, which is session. We
    need to know the start of the stack so that we could check for
    stack overruns.
  */
  session->thread_stack= (char*) &session;
  if (! session->initGlobals())
    return 0;
  bool isRegisterthread = false ;
  DatabaseManagerImpl* dbmgrimpl=0;
  for (;;)
  {
    if (! session->authenticate())
      goto end_thread;
    if(!isRegisterthread)
    {
      DatabaseManager* dbmgr = session->getDatabaseManager() ;
      dbmgrimpl = (DatabaseManagerImpl*) dbmgr ;
       if (OK != dbmgrimpl->registerThread())
      {
        db_error(ER_CANT_REGISTER_THREAD,MYF(0));
        goto end_thread;
      }
      ((DatabaseManagerImpl*)dbmgr)->setProcSlot();
      isRegisterthread= true ;
    }

    session->prepareForQueries();

    while (!session->net.error && session->net.vio != 0 &&
           !(session->killed == Session::KILL_CONNECTION))
    {
      if (! session->executeStatement())
         break;
    }

end_thread:
    session->disconnect(0, true);
    if(isRegisterthread)dbmgrimpl->deregisterThread();
    if (thread_scheduler.end_thread(session, 1))
    {
      return 0;                                 // Probably no-threads
    }
    /*
      If end_thread() returns, we are either running with
      thread-handler=no-threads or this thread has been schedule to
      handle the next connection.
    */
    session= current_session;
    session->thread_stack= (char*) &session;
  }
}
